JavaScript -- apply, call & bind
apply&call
对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2]);
数组之间追加
var array1 = [12, "foo", {name: "Joe"}, -2458];
var array2 = ["Doe", 555, 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为 [12, "foo", {name: "Joe"}, -2458, "Doe", 555, 100] */
获取数组中的最大值和最小值
var numbers = [5, 458, 120, -215 ];
var maxInNumbers = Math.max.apply(Math, numbers); //458
var maxInNumbers = Math.max.call(Math, 5, 458, 120, -215); //458
number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。
验证是否是数组(前提是toString()方法没有被重写过)
functionisArray(obj) {
returnObject.prototype.toString.call(obj) === '[object Array]';
}
类(伪)数组使用数组方法
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
Javascript 中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagNae, document.childNodes 之类的,它们返回 NodeList 对象都属于伪数组,不能应用 Array下的 push, pop 等方法。
但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。
经典应用
定义一个 log 方法,让它可以代理 console.log 方法
function log(){
console.log.apply(console, arguments);
};
接下来的要求是给每一个 log 消息添加一个”(app)”的前辍
该怎么做比较优雅呢?这个时候需要想到 arguments 参数是个伪数组,我们可以通过 Array.prototype.slice.call
转化为标准数组,再使用数组方法 unshift 即可
function log() {
var args = Array.prototype.slice.call(arguments);
args.unshift('(app)');
console.log.apply(console, args);
};
bind
MDN的解释是:bind()方法会创建一个新函数
,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。
var bar = function() {
console.log(this.x);
}
var foo = {
x: 3
}
var sed = {
x: 4
}
bar(); // undefined
var func = bar.bind(foo);
func(); // 3
var func2 = bar.bind(foo).bind(sed);
func2(); // 3
apply, call, bind 对比
- apply, call, bind 三者都是用来改变函数的 this 对象的指向的
- apply, call, bind 三者第一个参数都是 this 要指向的对象,也就是想指定的上下文
- apply, call, bind 三者都可以利用后续参数传参
- bind是返回对应函数,便于稍后调用;apply, call则是立即调用